package top.java.reflectionTest;

import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.ElementType;
import java.util.Properties;

/**
 *      关于java.lang.Class类的理解
 *
 *      类的加载过程：
 *          程序经过java.exe命令以后，会生成一个或多个字节码文件(.class结尾)
 *          接着我们使用java.exe命令对某个字节码文件进行解锁运行
 *          相当于将某个字节码文件加载到内存中，此过程就称为类的加载。
 *          加载到内存中的类，我们就称为运行时类，此运行时类，就作为Class的一个实例
 *
 *          Class的实例就对应着一个运行时类
 *
 *          加载到内存中的运行时类，会缓存一定的时间，在此时间内，我们可以通过不同的方式来获取此运行时类
 */
public class ReflectionTest2 {

    /**
     *  获取Class实例的四种方式 (前三种方式需要掌握)
     */
    @Test
    public void test1() throws ClassNotFoundException {
        //方式一：调用运行时类的属性： .class
        Class<Person> class1 = Person.class;
        System.out.println(class1);             //class top.stmo.reflectionTest.Person

        //方式二：通过运行时类的对象： 调用getClass()方法
        Person p1 = new Person();
        Class class2 = p1.getClass();
        System.out.println(class2);

        //方式三：调用Class的静态方法： forName(String classPath)
        Class class3 = Class.forName("top.java.reflectionTest.Person");
        System.out.println(class3);

        System.out.println(class1 == class2);
        System.out.println(class1 == class3);
        System.out.println(class2 == class3);

        //方式四：使用类的加载器   (了解)
        ClassLoader classLoader = ReflectionTest2.class.getClassLoader();
        Class class4 = classLoader.loadClass("top.java.reflectionTest.Person");
        System.out.println(class4);

        System.out.println(class1 == class4);
    }

    /**
     *      哪些类型可以有Class对象？
     *
     *      class：外部类，成员(成员内部类，静态内部类)，局部内部类，匿名内部类
     *      interface：接口
     *      []：数组
     *      enum：枚举
     *      annotation：注解@interface
     *      primitive type：基本数据类型
     *      void
     */
    @Test
    public void test2() {
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = ElementType.class;
        Class c6 = Override.class;
        Class c7 = int.class;
        Class c8 = void.class;
        Class c9 = Class.class;
        int[] a = new int[10];
        int[] b = new int[100];
        Class c10 = a.getClass();
        Class c11 = b.getClass();
        //只要数组的元素类型与维度一样，就是同一个Class
        System.out.println(c10 == c11);
    }

    /**
     *      类加载器作用是用来把类(class)装载进内存的。
     *
     *      引导类加载器：用C++编写的，是JVM自带的类加载器，负责Java平台核心库，用来装载核心类
     *                 库。该加载器无法直接获取
     *
     *      扩展类加载器：负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
     *
     *      系统类加载器：负责java –classpath 或 –Djava.class.path所指的目录下的类与jar包装入工
     *                 作 ，是最常用的加载器
     *
     *
     */
    @Test
    public void test3() {

        //对于自定义类，使用系统类加载器加载
        //获取当前自定义类的类的加载器
        ClassLoader classLoader1 = ReflectionTest2.class.getClassLoader();
        System.out.println(classLoader1);        //系统类加载器

        //调用系统类加载器的getParent()：获取扩展类加载器
        ClassLoader classLoader2 = classLoader1.getParent();       //上一层
        System.out.println(classLoader2);        //扩展类加载器

        //调用扩展类加载器的getParent()：无法获取引导类加载器
        ClassLoader classLoader3 = classLoader2.getParent();       //获取上一层
        System.out.println(classLoader3);        //引导类加载器       无法直接获取
    }

    /**
     *      Properties：用来读取配置文件
     */
    @Test
    public void test4() throws IOException {
        InputStream ras = null;
        try {
            Properties properties = new Properties();

            //读取配置文件的方式一：此时的文件默认在当前的module下
//        FileInputStream fis = new FileInputStream("jdbc.properties");
//        properties.load(fis);

            //读取配置文件的方式二：此时的文件默认在当前module的src下
            ClassLoader classLoader = ReflectionTest2.class.getClassLoader();
            ras = classLoader.getResourceAsStream("../jdbc.properties");
            properties.load(ras);

            String username = properties.getProperty("username");
            String password = properties.getProperty("password");
            System.out.println(username + "：" + password);
        }catch (IOException e) {
            System.out.println(e.getMessage());
        }finally {
           if (ras != null) {
               ras.close();
           }
        }
    }

}
